/***************************************************************************
	debugstreams.h  - sets up debugging sinks
			
	Basically, dout(LEVEL) returns clog if the debug level is higher
	than LEVEL or a dummy ostream if lower
	The level can be set by dout.setLevel(LEVEL)

Usage:

Really Simple!

By including debugstream.h, you have access to the "dout" 
instance of the Debug class.
 
You use it just like cout, with a modifier for level

   dout.setLevel(ALL);  // print all levels of output

   dout(INFO) << "This is some information I wish to log\n";
	

   dout.setLevel(RELEASE);
	
   dout(ERR) << This error will be printed since it is important\n";
   dout(INFO) << "This info was meant just for the developer and "
              << "will not be printed at this debug level" << endl;
	           
***************************************************************************/

#ifndef DEBUGSTREAMS_H
#define DEBUGSTREAMS_H 1

#include <iostream>
//#include <fstream>
#include <string>
#include <vector>
#include <stack>

#include "CColor.h"

using namespace std;

#ifdef NDEBUG
// define this dummy macro
#define dout(X) 1 ? (void)0 : (void) cerr
#else

// create dummy stream which just dumps all output
// this is what we use if the DebugLevel is higher than the message importance
// this stream sets a failbit and hence won't attempt to process it's input
static ostream nullstream(NULL);


/**
 * @addtogroup Debug
 * @{
 */

/**
 * Décrit les différents niveau de sortie possible
 */
enum DebugLevelType
  {
    ALL, /**< Montre tout les messages */
    MEM, /**< Debugging ultra détaillé des fuites de mémoires */
    DEBUG, /**< Message de debugging */
    LFUNC, /**< Annonce de fonctions dans des boucles */
    LINFO, /**< Messages dans des boucles */
    FUNC, /**< Annonce de fonction */
    INFO, /**< Information générale */
    BLANK, /**< Idem, mais sans "    INFO  :" avant le message */
    PROGRESS, /**< - */
    RELEASE, /**< Truc à mettre dans un log même après une release */
    WARN, /**< Qqch est incorrect, fait biip */
    ERR, /**< Désastre, bip aussi */
    OFF /**< Pas d'affichage */
  };

/**
 * La classe Debug retourne clog si le niveau de sortie est plus haut que
 * le niveau interne, ou alors un flot vide.\n
 *
 * Le niveau peut être ajusté avec dout.setLevel(LEVEL)\n
 *
 * La classe stocke dans une pile le niveau courant, ce qui permet de
 * revenir au niveau précédent (une fois un appel de fonction terminé
 * par exemple).\n
 *
 * Je n'ai pas codé cette classe, mais modifiée pour la gestion des
 * couleurs et pour pouvoir avoir plusieurs niveaux de sorties
 * différents.
 *
 * @author Mr X
 * @author Johan "Solo" Luisier
 *
 * @code
 * dout.setLevel(DEBUG);
 *
 * dout(DEBUG) << "Une information de debugging" << endl;
 *
 * dout.setLevel(BLANK);
 *
 * dout(BLANK) << "Une information générale" << endl;
 * dout(MEM) << "Ne sera pas affiché" << endl;
 * @endcode
 */

class Debug
{
 public:
  /**
   * Constructeur par défaut, ne fait rien de spécial.
   */
  Debug ();
  /**
   * Destructeur, ne fait rien de spécial.
   */
  ~Debug();
  /**
   * Appelle clog si le niveau Level est supérieur ou égal à la valeur
   * stockée.
   *
   * @param[in] Level niveau de sortie désiré
   *
   * @return clog ou null
   */
  ostream& operator()(const DebugLevelType& Level);
  /**
   * Permet de fixer le niveau de courant.
   *
   * @param[in] Level niveau de sortie désiré
   */
  static void setLevel(const unsigned int& Level, const unsigned int&);
  /**
   * Affiche le niveau interne de sortie.
   */
  static string printLevel();
  /**
   * Couleur et attribut par défaut de la sortie
   * @see CColor
   */
  static CColor Default;
  /**
   * Permet de savoir si le flot est en cours d'utilisation.
   */
  static bool Active;
  /**
   * Change la valeur de Active.
   *
   * @param[in] value nouvelle valeur de Active.
   */
  static void toggle(const bool& value);
  // static void LogToFile(const char* LogFileName, const DebugLevelType& Level);
  /**
   * Permet d'ajouter un niveau de sortie sur la pile.\n
   *
   * L'argument par défaut permet d'empiler la valeur interne à la
   * classe.
   *
   * @param[in] level pointeur sur la valeur à ajouter, par défaut
   * Debug::itsLevel.
   */
  static void empileNiveau(const DebugLevelType* level = &Debug::itsLevel);
  /**
   * Permet de vider la pile d'un élément.
   */
  static void depileNiveau();
 private:
  /**
   * Permet de déterminer si les niveaux de sorties ont bien été
   * initialisés.
   */
  static bool niveauxLus;
  /**
   * Liste des noms des niveaux de sorties paramétrables.
   */
  static const vector< string > NomsNiveaux;
  //static void lireNiveaux();
  /**
   * Niveau interne de référence, càd si itsLevel est en INFO, tous
   * les niveau inférieurs (DEBUG, LFUNC, ...) ne seront pas affichés.
   */
  static DebugLevelType itsLevel;
  /**
   * Couleurs et attributs des différents niveaux de sortie.
   */
  static CColor Tab[12];
  /**
   * Pile constituée des niveaux de sorties successifs. Le niveau
   * actif set toujours le dernier (top()). La pile est augmentée de
   * manière intellignente : si on veut ajouter un élément égal au
   * précédent, alors on ajoute en fait rien.
   */
  static stack< const DebugLevelType* > historique;
  /**
   * Pile permettant de gérer qules éléments de historique doivent
   * être dépilés. Si l'ajout dans historique n'a pas lieu car le même
   * élément est déjà présent, alors on empile \e true dans
   * idemPrecedent, et la valeur ne sera pas dépilée ensuite.
   */
  static stack< bool > idemPrecedent;
};


/**
 * Une instance de la classe Debug, utilisée partout.
 */
static Debug dout;

/**
 * Appelle dout(ERR), racourci
 * @return clog ou null
 */
ostream& error();

/**
 * Appelle dout(WARN), racourci
 * @return clog ou null
 */
ostream& warning();

/**
 * Appelle dout(BLANK), racourci
 * @return clog ou null
 */
ostream& blank();

/**
 * Appelle dout(INFO), racourci
 * @return clog ou null
 */
ostream& info();

/**
 * Appelle dout(FUNC), racourci
 * @return clog ou null
 */
ostream& func();

/**
 * Appelle dout(LINFO), racourci
 * @return clog ou null
 */
ostream& linfo();

/**
 * Appelle dout(LFUNC), racourci
 * @return clog ou null
 */
ostream& lfunc();

/**
 * Appelle dout(DEBUG), racourci
 * @return clog ou null
 */
ostream& debug();

/**
 * Redéfinition de endl.
 *
 * @param[in] os flot quelconque
 *
 * @return os, modifié
 */
ostream& endreq(ostream& os);

/**
 * @}
 */

#endif // NDEBUG

#endif // DEBUGSTREAMS_H
